// -*- coding: utf-8 -*-

#include <iostream>
#include <string>

#include "Modeler.h"

using namespace std;
using namespace elisa;

class Falling : public Modeler
{
private:
	static const VNInterval G, K, E;

	ContVar py, vy;

	static Interval Time;
	static VNInterval PY, VY;

public:
	Falling()
		: Modeler(Time),
		  py(PY, "py"),
		  vy(VY, "vy")
	{
		elisa::RealVar::SetPrecision(1.0e-1);

		double p = 1.0e-1;
		t.setPrecision(std::max<double>(p, Time.width()*2));

		t.setDomain(new Interval(Time.inf(), 1.0e3));

		addVar(py);
		addVar(vy);
	}

	// 連続制約の定義
	template<typename T>
	static void flow(int n, T *dv, const T *v, T t, void *param)
	{
		// py
		dv[0] = v[1];
		// vy
		dv[1] = -G - K*v[1];
	}

	// ガード制約の定義
	template<typename T> 
	static T guard(const std::vector<T>& v, const T& t)
	{
		return v[0];
	}

	bool reset(bool first)
	{
		Interval nt(t.domain().inf(), t.domain().sup());
		VNInterval npy(py.domain().inf(), py.domain().sup());

		VNInterval pvy(vy.domain().inf(), vy.domain().sup());
		VNInterval nvy(-E*pvy);

		if (first) { // 最初に見つかった解
			if (Time.overlaps(nt))
				// 前フェーズの終了時間と重なっていた場合は更新しない
				return true;

			Time = nt;
			PY = npy;
			VY = nvy;

		} else { // 2つ目以降

			if (!Time.overlaps(nt))
				// これまでの解と重なりがない → 定性的に異なる解 (TODO)
				return false;

			Time  = hull(Time, nt);
			PY = vn::hull(PY, npy);
			VY = vn::hull(VY, nvy);
		}

		// 和をとった包囲を表示
		cout << '\t' << "nt " << " = " << Time  << endl;
		cout << '\t' << "npy" << " = " << PY << endl;
		cout << '\t' << "nvy" << " = " << VY << endl;

		return true;
	}

	void print(std::ostream& out) const
	{
		out << '\t' << t.getName()  << " = " << t.domain() << endl;
		out << '\t' << py.getName() << " = " << py.domain() << endl;
		out << '\t' << vy.getName() << " = " << vy.domain() << endl;
	}
};

MODEL(Falling)


const VNInterval Falling::G(VNInterval(98)/10);
const VNInterval Falling::K(VNInterval(3)/10);
const VNInterval Falling::E(1.0);

//Interval Falling::Time(Interval::Zero);
Interval   Falling::Time(0.0, 0.0);
VNInterval Falling::PY(10.0, 11.0);
VNInterval Falling::VY(10.0);
//VNInterval Falling::VY(0.0, 5.0);
